package com.zlk.algorithm.algorithm.dynamicPlan.twoDimension;

import java.io.*;
import java.util.Scanner;

// 节点数为n高度不大于m的二叉树个数
// 现在有n个节点，计算出有多少个不同结构的二叉树
// 满足节点个数为n且树的高度不超过m的方案
// 因为答案很大，所以答案需要模上1000000007后输出
// 测试链接 : https://www.nowcoder.com/practice/aaefe5896cce4204b276e213e725f3ea
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码，把主类名改成Main，可以直接通过
public class Code05_NodenHeightNotLargerThanm {


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while (in.nextToken() != StreamTokenizer.TT_EOF) {
            int n = (int) in.nval;
            in.nextToken();
            int m = (int) in.nval;
            out.println(compute2(n, m));
        }
        out.flush();
        out.close();
        br.close();
    }
    // 记忆化搜索
    public static int MAXN = 51;

    public static int MOD = 1000000007;

    // 记忆化搜索
    public static long[][] dp1 = new long[MAXN][MAXN];

    static {
        for (int i = 0; i < MAXN; i++) {
            for (int j = 0; j < MAXN; j++) {
                dp1[i][j] = -1;
            }
        }
    }
    /**
     * n个数   m长度
     * 头节点确定
     *      左数全部为剩下的 n-1  并且<m-1     哪右数就是0  小于 0
     *      左数为n-2    右数为 1 < 1
     *      左数为n-3    右数  2<2
     * @param n
     * @param m
     * @return
     */
    // 二叉树节点数为n
    // 高度不能超过m
    // 结构数返回
    // 记忆化搜索
    public static int compute1(int n, int m) {
        if (n == 0) {
            return 1;
        }
        // n > 0
        if (m == 0) {
            return 0;
        }
        if (dp1[n][m] != -1) {
            return (int) dp1[n][m];
        }
        long ans = 0;
        // n个点，头占掉1个
        for (int k = 0; k < n; k++) {
            // 一共n个节点，头节点已经占用了1个名额
            // 如果左树占用k个，那么右树就占用i-k-1个
            ans = (ans + ((long) compute1(k, m - 1) * compute1(n - k - 1, m - 1)) % MOD) % MOD;
        }
        dp1[n][m] = ans;
        return (int) ans;
    }

    // 严格位置依赖的动态规划
    public static long[][] dp2 = new long[MAXN][MAXN];
    /**
     * 动态规划
     * @param n
     * @param m
     * @return
     */
    public static int compute2(int n, int m) {
        for (int i = 0; i <= m; i++) {
            dp2[0][i] =1;
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                long ans = 0;
                for (int k = 0; k < i; k++) {
                    ans = (ans + ((long) dp2[k][j-1] * dp2[i-k-1][j-1]) % MOD) % MOD;
                }
                dp2[i][j] =ans;
            }
        }
        return (int)dp2[n][m];
    }


}
